package com.bdf.congcache.core.memory;

import com.bdf.congcache.core.CacheStatus;
import com.bdf.congcache.core.control.ContextCache;
import com.bdf.congcache.core.memory.utils.MemoryElementDescriptor;
import com.bdf.congcache.core.model.ICacheElement;
import com.bdf.congcache.core.model.IContextCacheAttributes;
import com.bdf.congcache.core.status.IStatElement;
import com.bdf.congcache.core.status.IStats;
import com.bdf.congcache.core.status.StatElement;
import com.bdf.congcache.core.status.Stats;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public abstract class AbstractMemoryCache<K,V> implements IMemoryCache<K,V> {

    private static final Log log = LogFactory.getLog(AbstractMemoryCache.class);

    // 缓存上下文 属性
    private IContextCacheAttributes cacheAttributes;

    // 缓存上下文
    private ContextCache<K, V> cache;

    // 缓存状态
    private CacheStatus status;

    // 数据块的大小
    protected int chunkSize;

    // k 用户输入的缓存K   value为双向缓存的链表值 内部包含了element为链表当听值
    protected Map<K, MemoryElementDescriptor<K, V>> map;
    // 重入锁
    protected final Lock lock = new ReentrantLock();

    // 命令次数
    protected AtomicLong hitCnt;

    // 丢失次数
    protected AtomicLong missCnt;

    // 添加次数
    protected AtomicLong putCnt;

    @Override
    public void initialize(ContextCache<K, V> hub)
    {
        hitCnt = new AtomicLong(0);
        missCnt = new AtomicLong(0);
        putCnt = new AtomicLong(0);

        this.cacheAttributes = hub.getCacheAttributes();
        this.chunkSize = cacheAttributes.getSpoolChunkSize();
        this.cache = hub;

        // 调用了抽象方法，由继承去实现  创建文件描述集合
        this.map = createMap();

        this.status = CacheStatus.ALIVE;
    }

    public abstract Map<K, MemoryElementDescriptor<K, V>> createMap();

    @Override
    public abstract boolean remove(K key) throws IOException;

    @Override
    public abstract ICacheElement<K, V> get(K key) throws IOException;


    /**
     * @Author 田培融
     * @Description 获取多个缓存值
     * @Date 10:57 2019/7/12
     * @Param [keys]
     * @return java.util.Map<K,com.bdf.congcache.core.model.ICacheElement<K,V>>
     **/
    @Override
    public Map<K, ICacheElement<K, V>> getMultiple(Set<K> keys) throws IOException
    {
        Map<K, ICacheElement<K, V>> elements = new HashMap<K, ICacheElement<K, V>>();

        if (keys != null && !keys.isEmpty())
        {
            for (K key : keys)
            {
                // 调用抽像方法
                ICacheElement<K, V> element = get(key);

                if (element != null)
                {
                    elements.put(key, element);
                }
            }
        }

        return elements;
    }


    @Override
    public abstract void update(ICacheElement<K, V> ce) throws IOException;

    @Override
    public abstract Set<K> getKeySet();


    @Override
    public void removeAll() throws IOException
    {
        map.clear();
    }


    @Override
    public void dispose() throws IOException
    {
        removeAll();
        hitCnt.set(0);
        missCnt.set(0);
        putCnt.set(0);
        log.info("Memory Cache dispose called.");
    }


    /**
     * @Author 田培融
     * @Description 对缓存使用情况进行统计
     * @Date 11:08 2019/7/12
     **/
    @Override
    public IStats getStatistics()
    {
        IStats stats = new Stats();
        stats.setTypeName("Abstract Memory Cache");

        ArrayList<IStatElement<?>> elems = new ArrayList<IStatElement<?>>();
        stats.setStatElements(elems);

        elems.add(new StatElement<AtomicLong>("Put Count", putCnt));
        elems.add(new StatElement<AtomicLong>("Hit Count", hitCnt));
        elems.add(new StatElement<AtomicLong>("Miss Count", missCnt));
        elems.add(new StatElement<Integer>("Map Size", Integer.valueOf(getSize())));

        return stats;
    }

    /***
     * @Author 田培融
     * @Description 循环取一遍值 ，打印
     * @Date 15:32 2019/7/12
     **/
    public void mapProbe()
    {
        log.debug("mapProbe");
        for (Map.Entry<K, MemoryElementDescriptor<K, V>> e : map.entrySet())
        {
            MemoryElementDescriptor<K, V> me = e.getValue();
            log.debug("mapProbe> key=" + e.getKey() + ", val=" + me.getCacheElement().getVal());
        }
    }

    /**
     * @Author 田培融
     * @Description 获取缓存上下文的属性名称
     * @Date 13:20 2019/7/12
     **/
    public String getCacheName()
    {
        String attributeCacheName = this.cacheAttributes.getCacheName();
        if (attributeCacheName != null)
        {
            return attributeCacheName;
        }
        return cache.getCacheName();
    }


    @Override
    public int getSize()
    {
        return this.map.size();
    }


    /**
     * @Author 田培融
     * @Description  获取一个缓存值ICacheElement<K,V>
     * @Date 15:55 2019/7/12
     **/
    @Override
    public ICacheElement<K, V> getQuiet(K key) throws IOException
    {
        ICacheElement<K, V> ce = null;

        MemoryElementDescriptor<K, V> me = map.get(key);
        if (me != null)
        {
            if (log.isDebugEnabled())
            {
                log.debug(getCacheName() + ": MemoryCache quiet hit for " + key);
            }

            ce = me.getCacheElement();
        }
        else if (log.isDebugEnabled())
        {
            log.debug(getCacheName() + ": MemoryCache quiet miss for " + key);
        }

        return ce;
    }


    /**
     * @Author 田培融
     * @Description  刷新磁盘
     * @Date 16:14 2019/7/12
     **/
    @Override
    public void waterfal(ICacheElement<K, V> ce)
    {
        this.cache.spoolToDisk(ce);
    }


    // 获取缓存上下文属性
    @Override
    public IContextCacheAttributes getCacheAttributes()
    {
        return this.cacheAttributes;
    }

    /**
     * @Author 田培融
     * @Description  给缓存上下文赋值
     * @Date 16:20 2019/7/12
     **/
    @Override
    public void setCacheAttributes(IContextCacheAttributes cattr)
    {
        this.cacheAttributes = cattr;
    }

    @Override
    public ContextCache<K, V> getContextCache()
    {
        return this.cache;
    }
}
